package inglar.autotc.client.ui;

import inglar.autotc.client.Utilities;
import inglar.autotc.client.HasContainingTreeItem;
import inglar.autotc.client.NodeLookup;
import inglar.autotc.client.resource.ResourceHandler;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Widget;

public class PageDevices extends Composite {
	
//	public boolean treeIsChanged = false;
//	public String currentDevice = "";
//	public String deviceAuthor = "";
	
	VerticalPanel contentPanel;
	ListBox comboBox;
	public Tree tree;
//	TreeDottedLine tree;
//	AsyncCallback<String> deviceListCallback;
	VerticalPanel vPanelTree;
	VerticalPanel vPanelConsole;
	VerticalPanel vPanelStruct;
	HTML lblDeviceDetails;
	VerticalPanel panelTreeDetails;
	PageDevices self;
	HTML lblTemplate;
	
//	public String loadedStruct = "";
//	public String settingName = "";
//	public String[] settingIds;
	
	public HTML htmlSettingDetails;
	public HTML htmlAuthorDetails;
	
	Button btnReload;
	Button btnApply;
	
	public PageDevices() {
		
		contentPanel = new VerticalPanel();
		initWidget(contentPanel);
		
		Label lblTemplateEditor = new Label("Template Editor");
		lblTemplateEditor.setStyleName("tabTitle");
		contentPanel.add(lblTemplateEditor);
		lblTemplateEditor.setText("");
		
		HorizontalPanel p1 = new HorizontalPanel();
		p1.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		contentPanel.add(p1);
		
		Label lblDevice = new Label("Device:");
		p1.add(lblDevice);
		lblDevice.getElement().setAttribute("style", "margin-right: 7px;");
		
		comboBox = new ListBox();
		p1.add(comboBox);
		
		btnReload = new Button("Reload");
		p1.add(btnReload);
		btnReload.getElement().setAttribute("style", "margin-left: 5px; margin-right: 5px;");
		btnReload.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				int s = comboBox.getSelectedIndex();
				if(s != 0) {
					loadDevice(comboBox.getItemText(s));
				} else {
					Window.alert("Nothing to reload.");
				}
			}
		});
		
		btnApply = new Button("Apply");
		p1.add(btnApply);
		btnApply.getElement().setAttribute("style", "margin-left: 5px; margin-right: 5px;");
		btnApply.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if(tree.getItemCount() == 0) {
					Window.alert("Select a device first.");
				}
				boolean confirm = Window.confirm("Save configuration to device '" + currentDevice + "'?" );
				if(confirm) applyTree();
			}
		});
		
		VerticalPanel p2 = new VerticalPanel();
		contentPanel.add(p2);
		
		Button load = new Button("Load tree...");
		p2.add(load);
		load.getElement().setAttribute("style", "margin-left: 5px; margin-right: 5px;");
		load.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if(currentDevice.isEmpty()) {
					Window.alert("Load a device first.");
				} else {
					try {
						DialogBoxSaveOrLoadTemplate tdb = new DialogBoxSaveOrLoadTemplate(self);
						tdb.load();
					} catch (Exception e) {
						Utilities.failureAlert("call getTemplateList #129", "method getTemplateList failure", e);
					} 
				}
			}
		});
		
		Button save = new Button("Save tree...");
		p2.add(save);
		save.getElement().setAttribute("style", "margin-left: 5px; margin-right: 5px;");
		save.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if(treeSaved) {
					if(currentDevice.isEmpty()) {
						Window.alert("Load a device first.");
					} else {
						DialogBoxSaveOrLoadTemplate tdb = new DialogBoxSaveOrLoadTemplate(self);
						tdb.save();
					}
				} else {
					Window.alert("Click APPLY first.");
				}
			}
		});
		
		comboBox.addItem("Loading . . .");
		
		AsyncCallback<String[]> deviceListCallback = new AsyncCallback<String[]>() {
			@Override
			public void onSuccess(String[] result) {
				if(result.length == 0) {
					loadDevice(null); 
				} else {
					comboBox.clear();
					comboBox.addItem("Click to select");
					for(int i = 0; i < result.length; i++) comboBox.addItem(result[i]);
				}
			}
			@Override
			public void onFailure(Throwable caught) {
				Utilities.failureAlert("deviceListCallback", "RPC failure", caught);
			}
		};
		
		String userid = Cookies.getCookie("QID");
		Utilities.rpc.getDeviceList(userid, deviceListCallback);
		
		comboBox.addMouseDownHandler(new MouseDownHandler() {
			@Override
			public void onMouseDown(MouseDownEvent event) {
				comboBox.setSelectedIndex(0);
			}
		});
		
		comboBox.addMouseUpHandler(new MouseUpHandler() {
			@Override
			public void onMouseUp(MouseUpEvent event) {
				int s = comboBox.getSelectedIndex();
				if(s != 0) {
					loadDevice(comboBox.getItemText(s));
				} 
			}
		});
		
		comboBox.addKeyUpHandler(new KeyUpHandler() {
			@Override
			public void onKeyUp(KeyUpEvent event) {
				int s = comboBox.getSelectedIndex();
				if(s != 0) {
					loadDevice(comboBox.getItemText(s));
				} 
			}
		});
		
		lblDeviceDetails = new HTML();
		contentPanel.add(lblDeviceDetails);
		lblDeviceDetails.getElement().setClassName("tslblDeviceDetails");
		
		vPanelStruct = new VerticalPanel();
		contentPanel.add(vPanelStruct);
		
		vPanelTree = new VerticalPanel();
		vPanelStruct.add(vPanelTree);
		vPanelTree.getElement().setClassName("tspanelTree");
		
		vPanelConsole = new VerticalPanel();
		vPanelStruct.add(vPanelConsole);
		vPanelConsole.getElement().setClassName("tspanelConsole");
		vPanelConsole.add(new HTML("Select a device."));
		
		panelTreeDetails = new VerticalPanel();
		vPanelTree.add(panelTreeDetails);
		panelTreeDetails.getElement().setClassName("tslblTreeDetails");
		
		htmlSettingDetails = new HTML();
		htmlAuthorDetails = new HTML();
		
		tree = new Tree();
//		tree = new TreeDottedLine();
		vPanelTree.add(tree);
		tree.setAnimationEnabled(true);
		
		this.self = this;
	}
	
	private void loadDevice(final String device) {
		
		Utilities.showLoadingModal();
		
		if(device == null) {
			lblDeviceDetails.setText("No configurable devices found.");
		} else {
			
			tree.clear();
			tree.add(new Image(ResourceHandler.INSTANCE.fbLoadBlue()));
			
			vPanelConsole.clear();
			vPanelConsole.add(new Image(ResourceHandler.INSTANCE.fbLoadBlue()));
			
			Utilities.rpc.scriptase("ifconfig " + device, new AsyncCallback<String>() {
				@Override
				public void onSuccess(String result) {
					
					if(result.contains("Device not found")) {
						vPanelConsole.add(new HTML("<span style='color:red'>DEVICE NOT FOUND</span>"));
						return;
					}
					
					lblDeviceDetails.setHTML(Utilities.whiteSpace(result));
					
					vPanelConsole.clear();
					final String command = "tc -d qdisc show dev " + device + "; tc -d class show dev " + device + "; tc -d filter show dev " + device + ";";
					Utilities.rpc.scriptase(command, new AsyncCallback<String>() {
						@Override
						public void onSuccess(String result) {
							HTML html = new HTML("CURRENT DEVICE CONFIGURATION:<br/><span style='color: blue'># " + command + "</span><br/>" + Utilities.whiteSpace(result));
							html.getElement().setClassName("tslblDeviceDetails");
							vPanelConsole.add(html);
						}
						@Override
						public void onFailure(Throwable caught) {
							Utilities.failureAlert("TrafficStructure.loadDevice(...) #229", "RPC failure", caught);
						}
					});
					
					getTreeDetails(device);
				}
				@Override
				public void onFailure(Throwable caught) {
					tree.clear();
					
					Utilities.failureAlert("TrafficStructure.loadDevice(...) #239", "RPC failure", caught);
				}
			});
		}
		
		this.currentDevice = device;
	}
	
	public String currentDevice = "";
	private String settingid = "";
	private String settingname = "";
	private String authorid = "";
	private String authorname = "";
	private String timestamp = "";
	private String script = "";
	public boolean treeSaved = true;
	
	public void setTreeSaved(boolean isTreeSaved) {
		if(isTreeSaved) {
			
		} else {
			setSettingDetails("", "");
		}
		this.treeSaved = isTreeSaved;
	}
	
	public void setSettingDetails(String id, String name) {
		String html = "Setting: " + ((id.isEmpty())? "<i>custom</i>" : name);
		settingid = id;
		settingname = name;
		htmlSettingDetails.setHTML(html);
	}
	
	public void setAuthorDetails(String id, String name, String timestamp) {
		String html = "Author: " + name + " (" + timestamp + ")";
		htmlAuthorDetails.setHTML(html);
	}
	
	private void getTreeDetails(final String device) {
		panelTreeDetails.clear();
		tree.clear();
		currentDevice = device;
		
		AsyncCallback<String[]> getTreeDetailsCallback = new AsyncCallback<String[]>() {
			@Override
			public void onSuccess(String[] result) {
				if(result.length == 0) {
					panelTreeDetails.add(new HTML("The system has no data for this device. <br/>The contents in the CURRENT DEVICE CONFIGURATION section at the " +
							"bottom <br/>of the page may not match the tree show below."));
					
					Composite _c = NodeFactory.getNode("", device, self);
					TreeItem _t = NodeLookup.getParent(tree, _c);
					TreeItem c = null;
					if(_t == null) c = tree.addItem(_c);
					else c = _t.addItem(_c);
					
					if(_c instanceof HasContainingTreeItem) {
						HasContainingTreeItem hcti = (HasContainingTreeItem) _c;
						hcti.setContainer(c);
					} else {
//						throw new RuntimeException("Unsupported object found while parsing data. Class: " + c.getClass().toString());
					}
					
					c.setState(true);
					
					Utilities.hideLoadingModal();
				} else if(result.length == 6) {
					
					int i = 0;
					script = result[i++];
					settingid = result[i++];
					settingname = result[i++];
					authorid = result[i++];
					authorname = result[i++];
					timestamp = result[i++];
					
					panelTreeDetails.add(htmlSettingDetails);
					panelTreeDetails.add(htmlAuthorDetails);
					
					setSettingDetails(settingid, settingname);
					setAuthorDetails(authorid, authorname, timestamp);
					
					String[] tc = script.trim().split(";");
					populateTree(tc, device);
					
					Utilities.hideLoadingModal();
					setTreeSaved(true);
				} else {
					String msg = "";
					for(int i = 0; i < result.length; i++) msg += "{" + ((result[i] == null)? "{NULL}" : result[i]) + "}\n";
					Utilities.failureAlert("TrafficStructure.getTreeDetails(...) #329", "result array length is not 6\n" + msg, new RuntimeException("result array length is not 6"));
				}
			}
			@Override
			public void onFailure(Throwable caught) {
				Utilities.failureAlert("getTreeDetailsCallback", "RPC failure", caught);
			}
		};
		
		String userid = Cookies.getCookie("QID");
		Utilities.rpc.getDeviceConfig(userid, device, getTreeDetailsCallback);
	}
	
	public void populateTree(String[] tc, String device) {
		tree.clear();
		try {
			for(int i = 0; i < tc.length; i++) {
				String cmd = tc[i].trim(); 
//				if(cmd.isEmpty()) continue;
				Composite c = NodeFactory.getNode(cmd, device, self);
				TreeItem ti = NodeLookup.getParent(tree, c);
				
				if(ti == null) { 
					TreeItem container = tree.addItem(c);
					if(c instanceof HasContainingTreeItem) {
						HasContainingTreeItem hcti = (HasContainingTreeItem) c;
						hcti.setContainer(container);
					} else {
//						throw new RuntimeException("Unsupported object found while parsing data. Class: " + c.getClass().toString());
					}
				}
				else {
					TreeItem container = ti.addItem(c);
					ti.setState(true);
					if(c instanceof HasContainingTreeItem) {
						HasContainingTreeItem hcti = (HasContainingTreeItem) c;
						hcti.setContainer(container);
					} else {
//						throw new RuntimeException("Unsupported object found while parsing data. Class: " + c.getClass().toString());
					}
				}
				
				setTreeSaved(true);
				
//				break;
			}
		} catch (Throwable caught) {
			Utilities.failureAlert("TrafficStructure.populateTree(...)", "UI component generation failure", caught);
		}
	}
	
	private void applyTree() {
		
		final String cmd = _fork(tree.getItem(0));
		
		Utilities.showLoadingModal();
		
		AsyncCallback<String> applyTreeCallback = new AsyncCallback<String>() {
			@Override
			public void onSuccess(String result) {
				if(!result.isEmpty()) Window.alert(result);
//				else {
					loadDevice(currentDevice);
//				}
//				dialogBoxPleaseWait.hide();
			}
			@Override
			public void onFailure(Throwable caught) {
				Utilities.failureAlert("applyTreeCallback", "Unable to set device tc configuration.", caught);
			}
		};
		
		String userid = Cookies.getCookie("QID");
		Utilities.rpc.setDeviceConfig(userid, currentDevice, settingid, cmd, applyTreeCallback);
	}
	
	public String _fork(TreeItem ti) {
		String retval = "";
		
		Widget w = ti.getWidget();
		if(w instanceof NodeQDisc) {
			NodeQDisc nq = (NodeQDisc) w;
			retval += nq.getScript();
		} else if(w instanceof NodeClass) {
			NodeClass nc = (NodeClass) w;
			retval += nc.getScript();
		} else if(w instanceof NodeFilter) {
			NodeFilter nf = (NodeFilter) w;
			retval += nf.getScript();
		} else {
			Window.alert("applyTree.fork found unsupported instance " + w.getClass().toString());
		}
		
		if(retval.trim().isEmpty()) retval += "";
		else retval += "; ";
//		retval += "\n";
		
		int items = ti.getChildCount();
		for(int i  = 0; i < items; i++) {
			retval += _fork(ti.getChild(i)) + " ";
		}
		
		return retval;
	}
	
}
